﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace Apollo
{
    public partial class Application : HttpApplication
    {
        public const string USER_VIEWS = "UserViews";

        public static Entity MetaEntity(int ID) {
            return Meta<Entity>()
                .Where(e => e.Id == ID)
                .SingleOrDefault();
        }

        public static Entity MetaEntity(string entityName) {
            return Meta<Entity>()
                .Where(e => e.Name == entityName)
                .SingleOrDefault();
        }

        public static EntityProperty MetaProperty(int ID) {
            return Meta<EntityProperty>()
                    .Where(e => e.Id == ID)
                    .SingleOrDefault();
        }

        public static EntityProperty MetaProperty(string entityName, string propertyName)
        {
            return Meta<EntityProperty>()
                    .Where(e => e.Entity.Name == entityName && e.PropertyName == propertyName)
                    .SingleOrDefault();
        }

        public static IEnumerable<T> Meta<T>() where T : class
        {
            if (typeof(T) == typeof(Enum))
                return (IEnumerable<T>)MetaEnum();

            string cacheKey = "APOLLO_CACHE_KEY_" + typeof(T).Name;

            if (HttpContext.Current.Application[cacheKey] != null){
                if (((IEnumerable<T>)HttpContext.Current.Application[cacheKey]).Count() > 0) {
                    return (IEnumerable<T>)HttpContext.Current.Application[cacheKey];
                } else {
                    HttpContext.Current.Application.Remove(cacheKey);
                }
            }

            // get it from db
            int rowCount = CurrentSession.QueryOver<T>().RowCount();

            if (rowCount > 10000)
            {
                // do not store if in the cache
                return CurrentSession
                    .QueryOver<T>()
                    .List<T>();
            }

            IList<T> results = CurrentSession
                    .QueryOver<T>()
                    .List<T>();

            HttpContext.Current.Application.Add(cacheKey, results);

            if (typeof(T) == typeof(Entity)
                || typeof(T) == typeof(EntityProperty)
                || typeof(T) == typeof(EntityView)
                || typeof(T) == typeof(EntityEdit)
                || typeof(T) == typeof(EntityAction)
                || typeof(T) == typeof(MenuItem)
                || typeof(T) == typeof(MenuItemAccess)
                || typeof(T) == typeof(Role)
                || typeof(T) == typeof(Position)
                || typeof(T) == typeof(EntityFilter)
                || typeof(T) == typeof(PenguinKPIDefinition))
                LoadApplicationMeta();

            return results;
        }

        public static void ClearCache()
        {
            HttpContext.Current.Application.Clear();
        }

        /// <summary>
        /// Cache enum with enum values
        /// </summary>
        /// <returns></returns>
        protected static IEnumerable<Enum> MetaEnum()
        {
            string cacheKey = "APOLLO_CACHE_KEY_ENUM";

            if (HttpContext.Current.Application[cacheKey] != null)
                return (IEnumerable<Enum>)HttpContext.Current.Application[cacheKey];

            IList<Enum> enums = CurrentSession.QueryOver<Enum>().List<Enum>();
            IList<EnumValue> enumValues = CurrentSession.QueryOver<EnumValue>().List<EnumValue>();

            foreach (Enum e in enums)
                e.Values = enumValues.Where<EnumValue>(ev => ev.Enum != null && ev.Enum.Id == e.Id)
                                        .ToList<EnumValue>();

            HttpContext.Current.Application.Add(cacheKey, enums);

            return enums;
        }

        protected static void LoadApplicationMeta()
        {
            string cacheKey = "APOLLO_CACHE_KEY_" + typeof(Entity).Name;

            if (HttpContext.Current.Application[cacheKey] != null)
                return;

            IEnumerable<Entity> entities = Meta<Entity>();
            IEnumerable<Language> languages = Meta<Language>();
            IEnumerable<Enum> enums = Meta<Enum>();
            IEnumerable<MenuItem> menuitems = Meta<MenuItem>();
            IEnumerable<Role> roles = Meta<Role>();

            foreach (EntityProperty property in Meta<EntityProperty>())
            {
                property.Entity = entities.SingleOrDefault<Entity>(e => e.Id == property.Entity.Id);

                if (property.BelongToEntity != null)
                    property.BelongToEntity = entities.SingleOrDefault<Entity>(e => e.Id == property.BelongToEntity.Id);
                if (property.Enum != null)
                    property.Enum = enums.SingleOrDefault<Enum>(e => e.Id == property.Enum.Id);
                if(property.ReverseRelationItemView != null)
                    property.ReverseRelationItemView = Meta<EntityView>().SingleOrDefault<EntityView>(v => v.Id == property.ReverseRelationItemView.Id);
                if (property.ReverseRelationListView != null)
                    property.ReverseRelationListView = Meta<EntityView>().SingleOrDefault<EntityView>(v => v.Id == property.ReverseRelationListView.Id);
                if (property.CustomView != null)
                    property.CustomView = Meta<EntityView>().SingleOrDefault<EntityView>(v => v.Id == property.CustomView.Id);
            }

            foreach (EntityView view in Meta<EntityView>())
                view.Entity = entities.SingleOrDefault<Entity>(e => e.Id == view.Entity.Id);

            foreach (EntityEdit edit in Meta<EntityEdit>())
                edit.Entity = entities.SingleOrDefault<Entity>(e => e.Id == edit.Entity.Id);

            foreach (EntityAction act in Meta<EntityAction>())
                act.Entity = entities.SingleOrDefault<Entity>(e => e.Id == act.Entity.Id);

            foreach (Entity entity in entities)
            {
                if (entity.DefaultView != null)
                    entity.DefaultView = Meta<EntityView>().SingleOrDefault<EntityView>(v => v.Id == entity.DefaultView.Id);
                if (entity.DefaultEdit != null)
                    entity.DefaultEdit = Meta<EntityEdit>().SingleOrDefault<EntityEdit>(e => e.Id == entity.DefaultEdit.Id);
            }

            foreach (MenuItem mi in menuitems)
            {
                if (mi.EntityView != null)
                    mi.EntityView = Meta<EntityView>().SingleOrDefault<EntityView>(v => v.Id == mi.EntityView.Id);
                if (mi.Parent != null)
                    mi.Parent = Meta<MenuItem>().SingleOrDefault<MenuItem>(m => m.Id == mi.Parent.Id);
            }

            foreach (MenuItemAccess mia in Meta<MenuItemAccess>())
            {
                mia.MenuItem = menuitems.SingleOrDefault<MenuItem>(mi => mi.Id == mia.MenuItem.Id);

                if(mia.Role != null)
                    mia.Role = Meta<Role>().SingleOrDefault<Role>(r => r.Id == mia.Role.Id);
            }

            foreach (EntityFilter ef in Meta<EntityFilter>()) {
                ef.Entity = entities.SingleOrDefault(e => e.Id == ef.Entity.Id);
                ef.Role = roles.SingleOrDefault(r => r.Id == ef.Role.Id);
            }
        }

        public static IEnumerable<UserView> CurrentUserViews {
            get {
                try
                {
                    if (HttpContext.Current.Session[USER_VIEWS] == null)
                    {
                        HttpContext.Current.Session.Add(
                            USER_VIEWS,
                            CurrentSession.QueryOver<UserView>()
                                .Where(uv => uv.User.Id == Apollo.Application.CurrentUser.Id)
                                .List<UserView>()
                        );
                    }

                    return HttpContext.Current.Session[USER_VIEWS] as IEnumerable<UserView>;
                } catch {
                    return null;
                }
            }
        }

        public static void ClearSession() {
            HttpContext.Current.Session.Remove(USER_VIEWS);
        }
    }
}
